package vm.baseNativeUseFunction;

import app.boot.BaseFunction;
import build.Bin.If;
import build.Bin.Val;
import build.Bin.Var;
import expression.BoolExpression;
import expression.MathExpression;
import expression.StringExpression;
import vm.baseapi.API;
import vm.baseapi.Println;
import vm.runtime.NextVMRuntime;
import vm.runtime.TypeParse;

import javax.print.attribute.standard.MediaSize;
import java.io.File;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringTokenizer;

import static app.boot.BaseFunction.getAllFilePaths;

public class NativeUseFunction {
    public static String[] NativeMethodList = {
            "system_time",
            "bool",
            "int",
            "long",
            "float",
            "double",
            "get_os",
            "get_login",
            "get_arch",
            "get_dir",
            "rm",
            "input",
            "str_to_bytes",
            "bytes_to_str",
            "get_file_bytes",
            "substr",
            "find",
            "lfind",
            "get_file_content",
            "get_script_dir",
            "len",
            "split",
            "list_rm",
            "list_add"
    };
    public static boolean IsNativeUseFunctionCode(String code) {
        try {
            String function = code.substring(0,code.indexOf("(")).trim();
            String content = code.substring(code.indexOf("(")+1 , code.lastIndexOf(")")).trim();
            return new ArrayList<String>(Arrays.asList(NativeMethodList)).contains(function);
        }catch (Exception e) {
            return false;
        }
    }
    public static boolean IsNativeUseFunction(String Name) {
        try {
            return new ArrayList<String>(Arrays.asList(NativeMethodList)).contains(Name);
        }catch (Exception e) {
            return false;
        }
    }
    public static Object run_code(String code , NextVMRuntime nextVMRuntime) throws Exception {
        String function = code.substring(0,code.indexOf("(")).trim();
        String content = code.substring(code.indexOf("(")+1 , code.lastIndexOf(")")).trim();
        String ContentRegex = ",(?=([^\"]*\"[^\"]*\")*[^\"]*$)(?=([^\\(]*\\([^\\)]*\\))*[^\\)]*$)";
        String[] tokens = content.split(ContentRegex);
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add(function);
        arrayList.addAll(Arrays.asList(tokens));
        return run(arrayList.toArray(new String[0]), nextVMRuntime , code);
    }
    public static Object run(String[] tokens , NextVMRuntime nextVMRuntime, String SourceCode)
        throws Exception {
        String Name = tokens[0];
        if (Name.equals("system_time")) {
            return System.currentTimeMillis();
        }
        else if (Name.equals("bool")) {
            return BoolExpression.getBool(tokens[1] , nextVMRuntime);
        }else if (Name.equals("int")) {
            return (int) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }
        else if (Name.equals("float")) {
            return (float) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }
        else if (Name.equals("double")) {
            return (double) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }
        else if (Name.equals("len")) {
            Object obj = StringExpression.getValueFromNextVM(tokens[1].trim(),nextVMRuntime);
            if (TypeParse.getType(obj).contains("[]")) {
                ArrayList<?> arrayList = (ArrayList<?>) obj;
                assert arrayList != null;
                return arrayList.size();
            }
            return StringExpression.getExpression(tokens[1],nextVMRuntime).length();
        }
        else if (Name.equals("long")) {
            return (long) MathExpression.evaluateExpression(tokens[1],nextVMRuntime);
        }else if (Name.equals("get_os")) {
            return System.getProperty("os.name");
        }else if (Name.equals("get_login")) {
            return System.getProperty("user.name");
        }else if (Name.equals("get_dir")) {
            return System.getProperty("user.dir");
        }else if (Name.equals("get_arch")) {
            return System.getProperty("os.arch");
        }
        else if (Name.equals("rm")) {
            File file = new File(StringExpression.getExpression(tokens[1],nextVMRuntime));
            if (file.isFile()) {
                return file.delete();
            } else {
                List<String> paths = getAllFilePaths(file.getAbsolutePath());
                for (String i : paths) {
                    System.out.println(i);
                    boolean ok = new File(i).delete();
                    if (!ok) {
                        return false;
                    }
                }
                return true;
            }
        }
        else if (Name.equals("get_file_bytes")) {
            File file = new File(StringExpression.getExpression(tokens[1],nextVMRuntime));
            return NextBytes.readFileToByteArray(file.getAbsolutePath());
        }
        else if (Name.equals("bytes_to_str")) {
            byte[] bytes = (byte[]) StringExpression.getValueFromNextVM(tokens[1].trim(),nextVMRuntime);
            return new String(bytes, Charset.forName(StringExpression.getExpression(tokens[2],nextVMRuntime)));
        }
        else if (Name.equals("str_to_bytes")) {
            return StringExpression.getExpression(tokens[1],nextVMRuntime).getBytes();
        }
        else if (Name.equals("substr")) {
            String str = StringExpression.getExpression(tokens[1],nextVMRuntime);
            int start = (int) MathExpression.evaluateExpression(tokens[2] , nextVMRuntime);
            int end = (int) MathExpression.evaluateExpression(tokens[3],nextVMRuntime);
            return str.substring(start , end);
        }
        else if (Name.equals("find")) {
            String str = StringExpression.getExpression(tokens[1],nextVMRuntime);
            int findIndex = (int) MathExpression.evaluateExpression(tokens[2] , nextVMRuntime);
            return str.indexOf(findIndex);
        }
        else if (Name.equals("lfind")) {
            String str = StringExpression.getExpression(tokens[1],nextVMRuntime);
            int findIndex = (int) MathExpression.evaluateExpression(tokens[2] , nextVMRuntime);
            return str.lastIndexOf(findIndex);
        }

        else if (Name.equals("get_file_content") ) {
            String name = StringExpression.getExpression(tokens[1],nextVMRuntime);
            return BaseFunction.getFileContent(name);
        }

        else if (Name.equals("get_script_dir"))
        {
            return nextVMRuntime.SourceFile.getParentFile().getAbsolutePath();
        }

        else if (Name.equals("split")) {
            String[] strings = StringExpression.getExpression(
                    tokens[1],nextVMRuntime).split(
                            StringExpression.getExpression(tokens[2],nextVMRuntime));
            return new ArrayList<>(Arrays.asList(strings));
        }
        else if (Name.equals("list_add")) {
            return list_add(tokens[1],tokens[2],nextVMRuntime);
        }
        else if (Name.equals("list_rm")) {
            if (StringExpression.IsNumber(tokens[2].trim())) {
                return list_rm_with_index(tokens[1],Integer.parseInt(tokens[2].trim()),nextVMRuntime);
            }
            else {
                return list_rm(tokens[1],tokens[2],nextVMRuntime);
            }
        }
        else {
            throw new Exception("Unknown Function: "+Name);
        }
    }
    public static ArrayList<Object> list_add(String list,String add,NextVMRuntime nextVMRuntime) {
        ArrayList<Object> obj_1 = (ArrayList<Object>) nextVMRuntime.values.get(list.trim());
        if (obj_1 != null) {
            obj_1.add(StringExpression.getValueFromNextVM(add.trim(),nextVMRuntime));
            return obj_1;
        } else if (nextVMRuntime.LastRuntime != null) {
            return list_add(list,add,nextVMRuntime.LastRuntime);
        }
        else {
            return null;
        }
    }

    public static ArrayList<Object> list_rm(String list,String add,NextVMRuntime nextVMRuntime) {
        ArrayList<Object> obj_1 = (ArrayList<Object>) nextVMRuntime.values.get(list.trim());
        if (obj_1 != null) {
            obj_1.remove(StringExpression.getValueFromNextVM(add.trim(),nextVMRuntime));
            return obj_1;
        } else if (nextVMRuntime.LastRuntime != null) {
            return list_rm(list,add,nextVMRuntime.LastRuntime);
        }
        else {
            return null;
        }
    }
    public static ArrayList<Object> list_rm_with_index(String list,int index,NextVMRuntime nextVMRuntime) {
        ArrayList<Object> obj_1 = (ArrayList<Object>) nextVMRuntime.values.get(list.trim());
        if (obj_1 != null) {
            obj_1.remove(index);
            return obj_1;
        } else if (nextVMRuntime.LastRuntime != null) {
            return list_rm_with_index(list,index,nextVMRuntime.LastRuntime);
        }
        else {
            return null;
        }
    }
}
